#include <prmOpt.h>
#include <memopt.h>
#include <sys/file.h>

static int prmInfInitI32(struct prmInfBaseStc *prmptr, const char *setname, int32_t *setValPtr, int32_t defval, uint32_t setlev)
{
    if (!(prmptr && setname && setValPtr))
    {
        logerrorc("");
        return -1;
    }
    prmptr->pname = setname;
    prmptr->valcomm = setValPtr;
    prmptr->strMaxLen = 0;
    prmptr->dv32 = defval;
    prmptr->cfgLev = setlev;
    prmptr->ptype = PRM_INF_VAL_TYPE_32;
    return 1;
}
static int prmInfInitI64(struct prmInfBaseStc *prmptr, const char *setname, int64_t *setValPtr, int64_t defval, uint32_t setlev)
{
    if (!(prmptr && setname && setValPtr))
    {
        logerrorc("");
        return -1;
    }
    prmptr->pname = setname;
    prmptr->valcomm = setValPtr;
    prmptr->strMaxLen = 0;
    prmptr->dv64 = defval;
    prmptr->cfgLev = setlev;
    prmptr->ptype = PRM_INF_VAL_TYPE_64;
    return 1;
}
static int prmInfInitFloat(struct prmInfBaseStc *prmptr, const char *setname, float *setValPtr, float defval, uint32_t setlev)
{
    if (!(prmptr && setname && setValPtr))
    {
        logerrorc("");
        return -1;
    }
    prmptr->pname = setname;
    prmptr->valcomm = setValPtr;
    prmptr->strMaxLen = 0;
    prmptr->dvf = defval;
    prmptr->cfgLev = setlev;
    prmptr->ptype = PRM_INF_VAL_TYPE_FLOAT;
    return 1;
}
static int prmInfInitDouble(struct prmInfBaseStc *prmptr, const char *setname, double *setValPtr, double defval, uint32_t setlev)
{
    if (!(prmptr && setname && setValPtr))
    {
        logerrorc("");
        return -1;
    }
    prmptr->pname = setname;
    prmptr->valcomm = setValPtr;
    prmptr->strMaxLen = 0;
    prmptr->dvd = defval;
    prmptr->cfgLev = setlev;
    prmptr->ptype = PRM_INF_VAL_TYPE_DOUBLE;
    return 1;
}
static int prmInfInitStr(struct prmInfBaseStc *prmptr, const char *setname, char *setValPtr, uint32_t setMaxSize, const char *defval, uint32_t setlev)
{
    if (!(prmptr && setname && setValPtr))
    {
        logerrorc("");
        return -1;
    }
    prmptr->pname = setname;
    prmptr->valcomm = setValPtr;
    prmptr->strMaxLen = setMaxSize;
    prmptr->dvs = defval;
    prmptr->cfgLev = setlev;
    prmptr->ptype = PRM_INF_VAL_TYPE_STR;
    return 1;
}

static int prmInfResetVal(struct prmInfBaseStc *prmptr, uint32_t optlev)
{
    if (!(prmptr))
    {
        logdebugc("");
        return -1;
    }
    if (prmptr->cfgLev < optlev)
    {
        return 0;
    }
    switch (prmptr->ptype)
    {
    case PRM_INF_VAL_TYPE_32:
    {
        prmptr->val32[0] = prmptr->dv32;
        break;
    }
    case PRM_INF_VAL_TYPE_64:
    {
        prmptr->val64[0] = prmptr->dv64;
        break;
    }
    case PRM_INF_VAL_TYPE_FLOAT:
    {
        prmptr->valf[0] = prmptr->dvf;
        break;
    }
    case PRM_INF_VAL_TYPE_DOUBLE:
    {
        prmptr->vald[0] = prmptr->dvd;
        break;
    }
    case PRM_INF_VAL_TYPE_STR:
    {
        memcpyStr(prmptr->valstr, prmptr->strMaxLen, prmptr->dvs);
        break;
    }
    default:
    {
        logdebugc("error type:%d", prmptr->ptype);
        return -1;
    }
    }
    return 1;
}

static int prmInfSetStrVal(struct prmInfBaseStc *prmptr, const char *setStrVal, uint32_t optlev)
{
    int tmpflag = 0;
    if (!(prmptr && setStrVal))
    {
        logdebugc("");
        return -1;
    }
    if (prmptr->cfgLev < optlev)
    {
        return 0;
    }
    switch (prmptr->ptype)
    {
    case PRM_INF_VAL_TYPE_32:
    {
        tmpflag = sscanf(setStrVal, "%d", prmptr->val32);
        break;
    }
    case PRM_INF_VAL_TYPE_64:
    {
        tmpflag = sscanf(setStrVal, "%lld", (long long *)prmptr->val64);
        break;
    }
    case PRM_INF_VAL_TYPE_FLOAT:
    {
        tmpflag = sscanf(setStrVal, "%f", prmptr->valf);
        break;
    }
    case PRM_INF_VAL_TYPE_DOUBLE:
    {
        tmpflag = sscanf(setStrVal, "%lf", prmptr->vald);
        break;
    }
    case PRM_INF_VAL_TYPE_STR:
    {
        memcpyStr(prmptr->valstr, prmptr->strMaxLen, setStrVal);
        tmpflag = 1;
        break;
    }
    default:
    {
        logdebugc("error type:%d", prmptr->ptype);
        return -1;
    }
    }
    if (tmpflag != 1)
    {
        logdebugc("set val fail[name=%s],strval=%s", prmptr->pname, setStrVal);
    }
    return tmpflag;
}

static int prmInfGetValStr(struct prmInfBaseStc *prmptr, char *outStr, uint32_t outlen, uint32_t optlev)
{
    int tmpflag = 1;
    if (!(prmptr && outStr))
    {
        logdebugc("");
        return -1;
    }
    if (prmptr->cfgLev < optlev)
    {
        return 0;
    }
    switch (prmptr->ptype)
    {
    case PRM_INF_VAL_TYPE_32:
    {
        snprintf(outStr, outlen, "%d", *prmptr->val32);
        break;
    }
    case PRM_INF_VAL_TYPE_64:
    {
        snprintf(outStr, outlen, "%lld", (long long)*prmptr->val64);
        break;
    }
    case PRM_INF_VAL_TYPE_FLOAT:
    {
        snprintf(outStr, outlen, "%f", *prmptr->valf);
        break;
    }
    case PRM_INF_VAL_TYPE_DOUBLE:
    {
        snprintf(outStr, outlen, "%lf", *prmptr->vald);
        break;
    }
    case PRM_INF_VAL_TYPE_STR:
    {
        memcpypro(outStr, outlen, prmptr->valstr, prmptr->strMaxLen);
        break;
    }
    default:
    {
        logdebugc("error type:%d", prmptr->ptype);
        return -1;
    }
    }
    return tmpflag;
}

static int prmMgrCmpCbk(const void *a, const void *b)
{
    struct prmInfBaseStc *tmpa = (struct prmInfBaseStc *)a;
    struct prmInfBaseStc *tmpb = (struct prmInfBaseStc *)b;
    return strcmp(tmpa->pname, tmpb->pname);
}

int prmMgrInit(struct prmMgrBaseStc *pMgrPtr, const char *cfgSavef , const char *cfgSwapf, const char *cfgFileLock)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    pMgrPtr->sortflag = 0;
    pMgrPtr->prmSaveFile = cfgSavef;
    pMgrPtr->prmSwapFile = cfgSwapf;
    pMgrPtr->prmSaveLock = cfgFileLock;
    pthread_mutex_init(&pMgrPtr->prmMgrLock, NULL);
    pMgrPtr->prmFileLockFd = open(pMgrPtr->prmSaveLock, O_CREAT | O_RDWR, 0666);
    logdebugc("parm init inf:%s,%s,%s, %d", pMgrPtr->prmSaveFile, pMgrPtr->prmSwapFile, pMgrPtr->prmSaveLock, pMgrPtr->prmFileLockFd);
    if (pMgrPtr->prmFileLockFd < 0)
    {
        logerrorc("open lock file error!\n");
    }
    // without lock.
    return cycleQueInit(&pMgrPtr->prmque, prmMgrCmpCbk, sizeof(struct prmInfBaseStc), 0);
}

int prmMgrFree(struct prmMgrBaseStc *pMgrPtr)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    logdebugc("parm do free!:%s,%s,%s, %d", pMgrPtr->prmSaveFile, pMgrPtr->prmSwapFile, pMgrPtr->prmSaveLock, pMgrPtr->prmFileLockFd);
    pthread_mutex_lock(&pMgrPtr->prmMgrLock);
    struct prmInfBaseStc tmpbase;
    while(cycleQuePullBack(&pMgrPtr->prmque, &tmpbase) > 0)
    {
        ;
    }
    cycleQueFree(&pMgrPtr->prmque);
    // lock file
    if (pMgrPtr->prmFileLockFd > 0)
    {
        close(pMgrPtr->prmFileLockFd);
        pMgrPtr->prmFileLockFd = -1;
    }
    pthread_mutex_unlock(&pMgrPtr->prmMgrLock);
    pthread_mutex_destroy(&pMgrPtr->prmMgrLock);
    return 1;
}

int prmMgrRegI32(struct prmMgrBaseStc *pMgrPtr, const char *setname, int32_t *setValPtr, int32_t defval, uint32_t setlev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc tmpinf;
    prmInfInitI32(&tmpinf, setname, setValPtr, defval, setlev);
    return cycleQuePushBack(&pMgrPtr->prmque, &tmpinf);
}

int prmMgrRegI64(struct prmMgrBaseStc *pMgrPtr, const char *setname, int64_t *setValPtr, int64_t defval, uint32_t setlev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc tmpinf;
    prmInfInitI64(&tmpinf, setname, setValPtr, defval, setlev);
    return cycleQuePushBack(&pMgrPtr->prmque, &tmpinf);
}

int prmMgrRegFloat(struct prmMgrBaseStc *pMgrPtr, const char *setname, float *setValPtr, float defval, uint32_t setlev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc tmpinf;
    prmInfInitFloat(&tmpinf, setname, setValPtr, defval, setlev);
    return cycleQuePushBack(&pMgrPtr->prmque, &tmpinf);
}

int prmMgrRegDouble(struct prmMgrBaseStc *pMgrPtr, const char *setname, double *setValPtr, double defval, uint32_t setlev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc tmpinf;
    prmInfInitDouble(&tmpinf, setname, setValPtr, defval, setlev);
    return cycleQuePushBack(&pMgrPtr->prmque, &tmpinf);
}

int prmMgrRegStr(struct prmMgrBaseStc *pMgrPtr, const char *setname, char *setValPtr, uint32_t setMaxSize, const char *defval, uint32_t setlev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc tmpinf;
    prmInfInitStr(&tmpinf, setname, setValPtr, setMaxSize, defval, setlev);
    return cycleQuePushBack(&pMgrPtr->prmque, &tmpinf);
}

int prmMgrRegFinish(struct prmMgrBaseStc *pMgrPtr)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    pMgrPtr->sortflag = 1;
    if (cycleQueSort(&pMgrPtr->prmque) < 0)
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
    for (size_t i = 0; i < cycleQueSize(&pMgrPtr->prmque); i++)
    {
        if (strlen(tmpPrmPtr[i].pname) >= PRM_STR_NAME_MAX_LEN)
        {
            logwarnc("param name long! %s", tmpPrmPtr[i].pname);
        }
        if (tmpPrmPtr[i].ptype == PRM_INF_VAL_TYPE_STR && tmpPrmPtr[i].strMaxLen > PRM_STR_VAL_MAX_LEN)
        {
            logwarnc("param str long! %s:%d", tmpPrmPtr[i].pname, tmpPrmPtr[i].strMaxLen);
        }
    }
    return 1;
}

int prmMgrReset(struct prmMgrBaseStc *pMgrPtr, uint32_t resetLev)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
    pthread_mutex_lock(&pMgrPtr->prmMgrLock);
    for (size_t i = 0; i < cycleQueSize(&pMgrPtr->prmque); i++)
    {
        prmInfResetVal(&tmpPrmPtr[i], resetLev);
    }
    pthread_mutex_unlock(&pMgrPtr->prmMgrLock);
    return 1;
}

int prmMgrPrintVals(struct prmMgrBaseStc *pMgrPtr)
{
    if (!(pMgrPtr))
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
    for (size_t i = 0; i < cycleQueSize(&pMgrPtr->prmque); i++)
    {
        char tmpValStr[PRM_STR_VAL_MAX_LEN];
        if (prmInfGetValStr(&tmpPrmPtr[i], tmpValStr, sizeof(tmpValStr), 0) <= 0)
        {
            logdebugc("get prm val str fail:%s", tmpPrmPtr[i].pname);
        }
        logdebugc("prm name=%s, prm val=%s", tmpPrmPtr[i].pname, tmpValStr);
    }
    return 1;
}

int prmMgrSetStrVal(struct prmMgrBaseStc *pMgrPtr, const char *setname, const char *setStrVal, uint32_t optlev)
{
    int tmprst = 0;
    if (!(pMgrPtr && setname && setStrVal))
    {
        logerrorc("");
        return -1;
    }
    if (!pMgrPtr->sortflag)
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
    struct prmInfBaseStc tmpinf;
    tmpinf.pname = setname;
    size_t tmpindex = cycleQueSearch(&pMgrPtr->prmque, &tmpinf);
    if (tmpindex == (size_t)-1)
    {
        logdebugc("not find name:%s", setname);
        return -1;
    }
    pthread_mutex_lock(&pMgrPtr->prmMgrLock);
    tmprst = prmInfSetStrVal(&tmpPrmPtr[tmpindex], setStrVal, optlev);
    pthread_mutex_unlock(&pMgrPtr->prmMgrLock);
    return tmprst;
}

int prmMgrGetStrVal(struct prmMgrBaseStc *pMgrPtr, const char *optname, char *optStrVal, uint32_t strMaxLen, uint32_t optlev)
{
    int tmprst = 0;
    if (!(pMgrPtr && optname && optStrVal))
    {
        logerrorc("");
        return -1;
    }
    if (!pMgrPtr->sortflag)
    {
        logerrorc("");
        return -1;
    }
    struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
    struct prmInfBaseStc tmpinf;
    tmpinf.pname = optname;
    size_t tmpindex = cycleQueSearch(&pMgrPtr->prmque, &tmpinf);
    if (tmpindex == (size_t)-1)
    {
        logdebugc("not find name:%s", optname);
        return -1;
    }
    pthread_mutex_lock(&pMgrPtr->prmMgrLock);
    tmprst = prmInfGetValStr(&tmpPrmPtr[tmpindex], optStrVal, strMaxLen, optlev);
    pthread_mutex_unlock(&pMgrPtr->prmMgrLock);
    return tmprst;
}

int prmMgrReadFile(struct prmMgrBaseStc *pMgrPtr)
{
    if (!(pMgrPtr && pMgrPtr->prmSaveFile))
    {
        logerrorc("");
        return -1;
    }
    if (!pMgrPtr->sortflag)
    {
        logerrorc("");
        return -1;
    }
    int rstFlag = 1;
    // lock
    flock(pMgrPtr->prmFileLockFd, LOCK_EX);
    FILE *tmpFd = fopen(pMgrPtr->prmSaveFile, "r");
    char *tmpPrmFindFlg = (char *)malloc(cycleQueSize(&pMgrPtr->prmque));
    do
    {
        if (tmpFd == NULL)
        {
            logerrorc("");
            rstFlag = -2;
            break;
        }
        if (!tmpPrmFindFlg)
        {
            logerrorc("");
            rstFlag = -3;
            break;
        }
        memset(tmpPrmFindFlg, 0, cycleQueSize(&pMgrPtr->prmque));
        char tmpbuf[PRM_STR_VAL_MAX_LEN + PRM_STR_NAME_MAX_LEN];
        struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
        while (fgets(tmpbuf, sizeof(tmpbuf), tmpFd) != NULL)
        {
            if (tmpbuf[0] == '#')
            {
                continue;
            }
            char *tmpFind = strchr(tmpbuf, '=');
            if (!tmpFind)
            {
                // logdebugc("not find =:%s", tmpbuf);
                continue;
            }
            tmpFind[0] = 0; //
            tmpFind++;      // skip =
            char *tmpEnterCheck = strchr(tmpFind, '\n');
            if (tmpEnterCheck)
            {
                tmpEnterCheck[0] = '\0';
            }
            struct prmInfBaseStc tmpinf;
            tmpinf.pname = tmpbuf;
            size_t tmpindex = cycleQueSearch(&pMgrPtr->prmque, &tmpinf);
            if (tmpindex == (size_t)-1)
            {
                logdebugc("not find name:%s", tmpbuf);
                continue;
            }
            if (tmpPrmFindFlg[tmpindex] == 1)
            {
                logdebugc("r param multi:%s", tmpbuf);
            }
            tmpPrmFindFlg[tmpindex] = 1;
            prmInfSetStrVal(&tmpPrmPtr[tmpindex], tmpFind, 0);
        }    
    } while (0);
    flock(pMgrPtr->prmFileLockFd, LOCK_UN);
    if (tmpFd)
    {
        fclose(tmpFd);
        tmpFd = NULL;
    }
    if (tmpPrmFindFlg)
    {
        free(tmpPrmFindFlg);
        tmpPrmFindFlg = NULL;
    }
    // unlock
    return rstFlag;
}

int prmMgrSaveFile(struct prmMgrBaseStc *pMgrPtr)
{
    if (!(pMgrPtr && pMgrPtr->prmSaveFile))
    {
        logerrorc("");
        return -1;
    }
    if (!pMgrPtr->sortflag)
    {
        logerrorc("");
        return -1;
    }
    int rstFlag = 1;
    // lock
    flock(pMgrPtr->prmFileLockFd, LOCK_EX);
    FILE *tmpFd = fopen(pMgrPtr->prmSaveFile, "r");
    FILE *tmpSaveFd = fopen(pMgrPtr->prmSwapFile, "w");
    char *tmpPrmFindFlg = (char *)malloc(cycleQueSize(&pMgrPtr->prmque));
    do
    {
        if (tmpFd == NULL)
        {
            logerrorc("");
            rstFlag = -1;
            break;
        }
        if (tmpSaveFd == NULL)
        {
            logerrorc("");
            rstFlag = -2;
            break;
        }
        if (!tmpPrmFindFlg)
        {
            logerrorc("");
            rstFlag = -3;
            break;
        }
        memset(tmpPrmFindFlg, 0, cycleQueSize(&pMgrPtr->prmque));
        char tmpbuf[PRM_STR_VAL_MAX_LEN + PRM_STR_NAME_MAX_LEN];
        char tmpValStr[PRM_STR_VAL_MAX_LEN];
        struct prmInfBaseStc *tmpPrmPtr = (struct prmInfBaseStc *)pMgrPtr->prmque.ptr;
        while (fgets(tmpbuf, sizeof(tmpbuf), tmpFd) != NULL)
        {
            if (tmpbuf[0] == '#')
            {
                fputs(tmpbuf, tmpSaveFd);
                continue;
            }
            char *tmpFind = strchr(tmpbuf, '=');
            if (!tmpFind)
            {
                // logdebugc("not find =:%s", tmpbuf);
                fputs(tmpbuf, tmpSaveFd);
                continue;
            }
            tmpFind[0] = 0; //
            struct prmInfBaseStc tmpinf;
            tmpinf.pname = tmpbuf;
            size_t tmpindex = cycleQueSearch(&pMgrPtr->prmque, &tmpinf);
            if (tmpindex == (size_t)-1)
            {
                logdebugc("not find name:%s", tmpbuf);
                tmpFind[0] = '=';
                fputs(tmpbuf, tmpSaveFd);
                continue;
            }
            if (tmpPrmFindFlg[tmpindex] == 1)
            {
                logdebugc("w param multi:%s", tmpbuf);
            }
            tmpPrmFindFlg[tmpindex] = 1;
            if (prmInfGetValStr(&tmpPrmPtr[tmpindex], tmpValStr, sizeof(tmpValStr), 0) < 0)
            {
                logdebugc("get val error!");
                tmpFind[0] = '=';
                fputs(tmpbuf, tmpSaveFd);
                continue;
            }
            fprintf(tmpSaveFd, "%s=%s\n", tmpPrmPtr[tmpindex].pname, tmpValStr);
        }
        for (size_t i = 0; i < cycleQueSize(&pMgrPtr->prmque); i++)
        {
            if (tmpPrmFindFlg[i] == 1)
            {
                continue;
            }
            if (prmInfGetValStr(&tmpPrmPtr[i], tmpValStr, sizeof(tmpValStr), 0) <= 0)
            {
                logdebugc("get val error!");
                continue;
            }
            fprintf(tmpSaveFd, "%s=%s\n", tmpPrmPtr[i].pname, tmpValStr);
        }
    } while (0);
    if (tmpFd)
    {
        fclose(tmpFd);
        tmpFd = NULL;
    }
    if (tmpSaveFd)
    {
        fclose(tmpSaveFd);
        tmpSaveFd = NULL;
    }
    if (tmpPrmFindFlg)
    {
        free(tmpPrmFindFlg);
        tmpPrmFindFlg = NULL;
    }
    rename(pMgrPtr->prmSwapFile, pMgrPtr->prmSaveFile);
    // unlock
    flock(pMgrPtr->prmFileLockFd, LOCK_UN);
    return rstFlag;
}
